#ifndef __LOG_H__
#define __LOG_H__

#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //c++17
#include <fstream>
#include <sstream>
#include <memory>
#include <mutex>
#include <unistd.h>

namespace LogModule
{
    const std::string sep = "\r\n";
    const std::string defaultpath = "./log";
    const std::string defaultname = "my.log";
    // 策略模式：C++多态特性
    // 1. 刷新策略：a：显示器打印 b：像向指定的文件写入
    // 刷新策略基类
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void syncLog(const std::string &message) = 0;
    };

    // 显示器打印日志的策略：子类
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        void syncLog(const std::string &message) override
        {
            std::lock_guard<std::mutex> guard(_mutex);
            std::cerr << message << sep;
        }

    private:
        std::mutex _mutex;
    };

    // 文件打印日志的策略：子类
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath,
                        const std::string &filename = defaultname)
            : _path(path), _filename(filename)
        {
            if (std::filesystem::exists(_path))
                return;
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        void syncLog(const std::string &message) override
        {
            std::lock_guard<std::mutex> guard(_mutex);
            std::string logname = _path + (_path.back() == '/' ? "" : "/") + _filename;
            std::fstream out(logname, std::ios::app);
            if (!out.is_open())
                return;
            out << message << sep;
            out.close();
        }

    private:
        std::string _path;     // 路径
        std::string _filename; // 文件名

        std::mutex _mutex;
    };

    // 日志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATEL
    };

    // 只有直接绑定到函数参数或显式临时对象的 const 引用才会延长生命周期，
    // 隐式构造的临时对象（如 return "DEBUG" 时的转换）不会被延长生命周期。
    const std::string LevelToStr(LogLevel &level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATEL:
            return "FATEL";
        default:
            return "UNKNOWN";
        }
    }

    // 获取时间
    std::string GetTimeStamp()
    {
        time_t tm = time(nullptr);
        struct tm ctime;
        // 可重入，线程安全的
        localtime_r(&tm, &ctime);
        // std::stringstream ss;
        // ss << ctime.tm_year + 1900
        //    << ctime.tm_mon
        //    << ctime.tm_mday
        //    << ctime.tm_hour
        //    << ctime.tm_min
        //    << ctime.tm_sec;
        // return ss.str();

        char timebuffer[64];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 ctime.tm_year + 1900,
                 ctime.tm_mon + 1,
                 ctime.tm_mday,
                 ctime.tm_hour,
                 ctime.tm_min,
                 ctime.tm_sec);
        return timebuffer;
    }

    // 2. 形成完整的日志 && 根据上面的策略，选择不同的刷新方式
    class Logger
    {
    public:
        // 内部类，表示未来的一条日志
        class LogMessage
        {
        public:
            LogMessage(LogLevel &level,
                       const std::string &srcname,
                       size_t lineno,
                       Logger &logger)
                : _current_time(GetTimeStamp()), _level(level), _pid(getpid()), _tid(gettid()), _srcname(srcname), _lineno(lineno), _logger(logger)
            {

                std::stringstream ss;
                ss << "[" << _current_time << "] "
                   << "[" << LevelToStr(_level) /*?*/ << "] "
                   << "[" << _pid << "] "
                   << "[" << _tid << "] "
                   << "[" << _srcname << "] "
                   << "[" << lineno << "] "
                   << "- ";
                _loginfo = ss.str();
            }

            template <typename T>
            LogMessage &operator<<(const T &message)
            {
                std::stringstream ss;
                ss << message;
                _loginfo += ss.str();
                return *this;
            }

            ~LogMessage()
            {
                _logger._strategy->syncLog(_loginfo);
            }

        private:
            std::string _current_time;
            LogLevel _level;
            pid_t _pid;
            pid_t _tid;
            std::string _srcname;
            size_t _lineno;
            std::string _loginfo; // 合并之后一条完整的消息

            Logger &_logger; // 引用外部类，方便进行策略刷新
        };

        Logger()
        {
            EnableConsoleLogStrategy();
            // EnableFileLogStrategy();
        };

        void EnableConsoleLogStrategy()
        {
            _strategy = std::make_unique<ConsoleLogStrategy>();
        }

        void EnableFileLogStrategy()
        {
            _strategy = std::make_unique<FileLogStrategy>();
        }

        // 仿函数
        LogMessage operator()(LogLevel type,
                              const std::string name,
                              size_t lineno)
        {
            return LogMessage(type, name, lineno, *this);
        }

        ~Logger() {};

    private:
        std::unique_ptr<LogStrategy> _strategy;
    };

    // 全局日志对象
    Logger logger;

// 使用宏，简化用户操作
#define LOG(level) logger(level, __FILE__, __LINE__)
#define ENABLE_CONSOLE_LOG_STRATEGY() logger.EnableConsoleLogStrategy()
#define ENABLE_FILE_LOG_STRATEGY() logger.EnableFileLogStrategy()
};

#endif
